package com.flash.javamate.utils;

import com.flash.javamate.entity.JavaFile;

import java.io.*;
import java.util.*;

/**
 * java文件解析.
 *
 * @author sunym1993
 * @version 0.0.1-SNAPSHOT
 * @since 2018年07月15日 16时00分
 */
public class JavaFileParser {

    public static JavaFile parse(String userDir, String classFullName) throws IOException {
        return parse(getRealPath(userDir, classFullName));
    }

    private static String getRealPath(String userDir, String classFullName) {
        StringBuilder sb = new StringBuilder();
        String separator = userDir.contains("/") ? "/" : "\\";
        if (userDir.endsWith(separator)) {
            sb.append(userDir);
        } else {
            sb.append(userDir).append(separator);
        }
        String classReferencePath = classFullName.replace(".", separator);
        sb.append(classReferencePath).append(".java");
        return sb.toString();
    }

    public static JavaFile parse(String filePath) throws IOException {
        File file = new File(filePath);
        return parse(file);
    }

    public static JavaFile parse(File file) throws IOException {
        JavaFile javaFile = new JavaFile();
        List<String> formateLines = getAllLinesAndformateAndRemoveCodeLines(file);
        parsePackage(formateLines, javaFile);
        parseImport(formateLines, javaFile);
        parseClass(formateLines, javaFile);
        parseField(formateLines, javaFile);
        parseMethod(formateLines, javaFile);
        return javaFile;
    }

    private static void parseMethod(List<String> formateLines, JavaFile javaFile) {
        List<JavaFile.JavaMethod> methods = javaFile.getMethods();
        Iterator<String> iterator = formateLines.iterator();

        boolean isNewMethod = true;
        JavaFile.JavaMethod javaMethod = new JavaFile().new JavaMethod();

        while (iterator.hasNext()) {
            String line = iterator.next();

            if (isNewMethod) {
                javaMethod = new JavaFile().new JavaMethod();
                isNewMethod = false;
            }

            if (line.startsWith("@")) {
                parseAndAddAnnotationLine(javaMethod.getAnnotations(), line);
                iterator.remove();
            } else if (line.startsWith("public")||line.startsWith("ApiResponse")){
                if (!line.contains("class")) {
                    parseMethodLine(javaMethod, line);
                    isNewMethod = true;
                    methods.add(javaMethod);
                }
                iterator.remove();
            }
        }
    }

    private static void parseMethodLine(JavaFile.JavaMethod javaMethod, String line) {

        Map<String, String> paramFields = javaMethod.getParamFields();

        if (line.indexOf("(") == -1) {
            return;
        }

        String methodPart = line.substring(0, line.indexOf("(")).trim();
//        String paramPart = line.substring(line.indexOf("(")+1,line.indexOf(")")).trim();

        String methodName = methodPart.substring(methodPart.lastIndexOf(" ")+1);
        javaMethod.setMethodName(methodName);

/*        methodPart = methodPart.substring(0, methodPart.indexOf(methodName)-1).trim();
        String returnField = methodPart.substring(methodPart.lastIndexOf(" ")+1);
        javaMethod.setReturnFields(returnField);

        if (!"".equals(paramPart)){
            String[] params = paramPart.split(",");
            for (String param : params) {
                param = param.trim();
                if (param.startsWith("@")) {
                    param = param.substring(param.indexOf(" ")+1);
                }
                String[] p = param.split(" ");
                paramFields.put(p[0].trim(), p[1].trim());
            }
        }*/

    }


    private static void parsePackage(List<String> formateLines, JavaFile javaFile) {
        Iterator<String> iterator = formateLines.iterator();
        while (iterator.hasNext()) {
            String line = iterator.next();
            if (line.startsWith("package")) {
                javaFile.setClassPackage(line.substring("package".length() + 1, line.length() - 1));
                iterator.remove();
                break;
            }
        }
    }

    private static void parseImport(List<String> formateLines, JavaFile javaFile) {
        Iterator<String> iterator = formateLines.iterator();
        Map<String, String> imports = javaFile.getImports();
        while (iterator.hasNext()) {
            String line = iterator.next();
            if (line.startsWith("import")) {
                String classFullName = line.substring("import".length() + 1, line.length() - 1);
                String className = classFullName.substring(classFullName.lastIndexOf(".")+1);
                imports.put(className, classFullName);
                iterator.remove();
            }
        }
        javaFile.setImports(imports);
    }

    private static void parseClass(List<String> formateLines, JavaFile javaFile) {
        Map<String, String> annotations = javaFile.getAnnotations();
        Iterator<String> iterator = formateLines.iterator();
        while (iterator.hasNext()) {
            String line = iterator.next();
            if (line.startsWith("@")) {
                parseAndAddAnnotationLine(annotations, line);
                iterator.remove();
            } else if (line.contains(" class ")){
                parseClassLine(javaFile, line);
                iterator.remove();
                break;
            }
        }
    }

    private static void parseClassLine(JavaFile javaFile, String line) {
        int startIndex = line.indexOf(" class ") + " class ".length();
        int endIndex = line.lastIndexOf("{");
        if (line.contains(" extends")) {
            endIndex = line.lastIndexOf(" extends");
        } else if (line.contains(" implements")) {
            endIndex = line.lastIndexOf(" implements");
        }
        String className = line.substring(startIndex, endIndex);
        javaFile.setClassName(className.trim());
    }

    private static void parseAndAddAnnotationLine(Map<String, String> annotations, String line) {
        if (!line.contains("(")) {
            String annotationName = line.substring("@".length());
            annotations.put(annotationName, null);
        } else {
            String annotationName = line.substring("@".length(), line.indexOf("("));
            String annotationValue = line.substring(line.indexOf("(")+1, line.lastIndexOf(")"));
            annotations.put(annotationName, annotationValue.replace("\"", ""));
        }
    }

    private static void parseField(List<String> formateLines, JavaFile javaFile) {
        Iterator<String> iterator = formateLines.iterator();
        Map<String, String> fields = javaFile.getFields();
        while (iterator.hasNext()) {
            String line = iterator.next();
            if (line.endsWith(";")) {
                parseFieldLine(fields, line);
                iterator.remove();
            }
        }
    }

    private static void parseFieldLine(Map<String, String> fields, String line) {
        if (line.contains("=")){
            line = line.substring(0, line.lastIndexOf("=")).trim();
        } else {
            line = line.substring(0, line.lastIndexOf(";")).trim();
        }
        String fieldName = line.substring(line.lastIndexOf(" ")+1);
        line = line.substring(0, line.lastIndexOf(fieldName)).trim();
        String fieldClass = line.substring(line.lastIndexOf(" ")+1);
        fields.put(fieldName, fieldClass);
    }




    // 只保留方法体，trim所有行，去掉空行
    private static List<String> getAllLinesAndformateAndRemoveCodeLines(File file) throws IOException {
        List<String> lines = new LinkedList<>();
        BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));

        String line = null;
        // 左大括号的数量，来一个右大括号时减少此数量
        int braceNum = 0;
        while ((line = reader.readLine()) != null) {
            line = line.trim();
            // 如果此时在一个方法的大括号中，不加入lines; 空行也不加lines
            if (braceNum <= 1 && !"".equals(line)) {
                lines.add(line);
            }

            char[] chars = line.toCharArray();
            for (char aChar : chars) {
                if (aChar == '{'){
                    braceNum++;
                }else if (aChar == '}'){
                    braceNum--;
                }
            }

        }
        return lines;
    }

}